package com.kindac.together.widget;

// Download by http://www.codefans.net
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.Region;
import android.graphics.drawable.GradientDrawable;
import android.os.Handler;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import com.kindac.together.common.Kin;

public abstract class PageWidget extends View {

    private int            mCornerX           = 0;                                          // 拖拽点对应的页脚
    private int            mCornerY           = 0;
    private Path           mPath0;
    private Path           mPath1;
    Bitmap                 mCurPageBitmap     = null;                                       // 当前页
    Bitmap                 mCurPageBackBitmap = null;
    Bitmap                 mNextPageBitmap    = null;

    PointF                 mTouch             = new PointF();                               // 拖拽点
    PointF                 mBezierStart1      = new PointF();                               // 贝塞尔曲线起始点
    PointF                 mBezierControl1    = new PointF();                               // 贝塞尔曲线控制点
    PointF                 mBeziervertex1     = new PointF();                               // 贝塞尔曲线顶点
    PointF                 mBezierEnd1        = new PointF();                               // 贝塞尔曲线结束点

    PointF                 mBezierStart2      = new PointF();                               // 另一条贝塞尔曲线
    PointF                 mBezierControl2    = new PointF();
    PointF                 mBeziervertex2     = new PointF();
    PointF                 mBezierEnd2        = new PointF();

    float                  mMiddleX;
    float                  mMiddleY;
    float                  mDegrees;
    float                  mTouchToCornerDis;
    ColorMatrixColorFilter mColorMatrixFilter;
    Matrix                 mMatrix;
    float[]                mMatrixArray       = { 0, 0, 0, 0, 0, 0, 0, 0, 1.0f };

    boolean                mIsRTandLB;                                                      // 是否属于右上左下
    // for test
    float                  mMaxLength         = (float) Math.hypot(Kin.mWidth, Kin.mHeight);
    int[]                  mBackShadowColors;
    int[]                  mFrontShadowColors;
    GradientDrawable       mBackShadowDrawableLR;
    GradientDrawable       mBackShadowDrawableRL;
    GradientDrawable       mFolderShadowDrawableLR;
    GradientDrawable       mFolderShadowDrawableRL;

    GradientDrawable       mFrontShadowDrawableHBT;
    GradientDrawable       mFrontShadowDrawableHTB;
    GradientDrawable       mFrontShadowDrawableVLR;
    GradientDrawable       mFrontShadowDrawableVRL;

    private Bitmap         mBitmap;
    private Canvas         mCanvas;
    private Paint          mBitmapPaint;
    Paint                  mPaint;

    // 翻页方向
    protected boolean      isRight            = false;
    // 翻页状态, 防止同时翻同一页n次
    protected boolean      isTuring           = false;
    // 判断翻页动作成立的初始坐标
    private float          startX             = 0;
    // 彩蛋计数
    protected int          kindac             = 0;

    /**
     * 翻页切换页面
     */
    public abstract void changed();

    /**
     * 最后一页
     */
    public abstract void startEngine();

    /**
     * 彩蛋
     * 
     * @param kindac
     */
    public abstract void startKindac(final int kindac);

    protected Handler mHandler = new Handler();
    Runnable          lTurn    = new Runnable() {

                                   public void run() {
                                       mCanvas.drawColor(0xFFAAAAAA);
                                       mTouch.x = mTouch.x - 50;
                                       if (mTouch.y < Kin.mHeight * 0.75) {
                                           mTouch.y += 40;
                                       }
                                       postInvalidate();

                                       // 类似定时刷新页面
                                       if (mTouch.x > -(Kin.mHeight * 2)) {
                                           mHandler.postDelayed(this, 25);
                                       } else {
                                           isTuring = false;
                                           Kin.curPage++;
                                       }

                                   }
                               };
    Runnable          rTurn    = new Runnable() {

                                   public void run() {
                                       mCanvas.drawColor(0xFFAAAAAA);
                                       mTouch.x = mTouch.x + 50;
                                       if (mTouch.y < Kin.mHeight / 2) {
                                           mTouch.y += 80;
                                       }
                                       postInvalidate();

                                       // 类似定时刷新页面
                                       if (mTouch.x < (Kin.mHeight * 2)) {
                                           mHandler.postDelayed(this, 25);
                                       } else {
                                           isTuring = false;
                                       }

                                   }
                               };

    public PageWidget(Context context) {
        super(context);
        mPath0 = new Path();
        mPath1 = new Path();
        createDrawable();

        mBitmap = Bitmap.createBitmap(Kin.mWidth, Kin.mHeight, Bitmap.Config.ARGB_8888);
        mCanvas = new Canvas(mBitmap);
        mBitmapPaint = new Paint(Paint.DITHER_FLAG);

        mPaint = new Paint();
        mPaint.setStyle(Paint.Style.FILL);

        ColorMatrix cm = new ColorMatrix();
        float array[] = { 0.55f, 0, 0, 0, 80.0f, 0, 0.55f, 0, 0, 80.0f, 0, 0, 0.55f, 0, 80.0f, 0, 0, 0, 0.2f, 0 };
        cm.set(array);
        mColorMatrixFilter = new ColorMatrixColorFilter(cm);

        mMatrix = new Matrix();
    }

    public void setBitmaps(Bitmap bm1, Bitmap bm2, Bitmap bm3) {
        mCurPageBitmap = bm1;
        mCurPageBackBitmap = bm2;
        mNextPageBitmap = bm3;
    }

    /**
     * 定时器
     */
    private void turnPage() {

        // 每一次翻页后都清空计数器
        kindac = 0;

        if (isRight) {
            isRight = false;
            mHandler.postDelayed(rTurn, 0);
        } else mHandler.postDelayed(lTurn, 0);

    }

    /**
     * Author : hmg25 Version: 1.0 Description : 计算拖拽点对应的拖拽脚
     */
    private boolean calcCornerXY(float x, float y) {

        // 这里把切换页面放到点击时, 即翻页动作前判断
        // 翻页后切换页面有问题
        // 反页中不再执行操作
        if (x <= Kin.mWidth / 3) {
            // 向右反页
            Log.i(PageWidget.class.getName(), "RRRRRRRRRRRRRRRR");

            isRight = true;

            if (Kin.curPage <= 0) {
                //
                // 当是第一页时.不能右翻
                //
                mTouch.x = -(Kin.mHeight * 2);
                return false;
            }

            //
            // 在翻页成功之后 切换页面
            //
            changed();
            mTouch.x = -Kin.mWidth / 2;

        } else if (x >= Kin.mWidth * 2 / 3) {
            // 向左反页
            Log.i(PageWidget.class.getName(), "LLLLLLLLLLLLLLLLL");

            isRight = false;

            if (Kin.curPage == Kin.page - 1) {

                isTuring = true;
                //
                // 当是最后一页时, 单独处理
                //
                startEngine();
                return false;
            }

            if (Kin.curPage != 0) {
                // 如果不是第一页,需要切换页面
                //
                // 在翻页成功之后 切换页面
                //
                changed();
            }
        } else {

            Log.i(PageWidget.class.getName(), "just here");
            startKindac(kindac++);
            return false;
        }
        mCornerX = Kin.mWidth;
        mCornerY = Kin.mHeight;
        return true;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        // 在翻页过程中,不执行任何操作
        if (isTuring) return false;

        if (event.getAction() == MotionEvent.ACTION_MOVE) {

            mCanvas.drawColor(0xFFAAAAAA);

            if (isRight) {
                mTouch.x = -Kin.mWidth / 2 + event.getX();
                mTouch.y = (float) (Kin.mHeight * 0.75);
            } else {
                mTouch.x = event.getX();
                mTouch.y = event.getY();
            }
            this.postInvalidate();
        }
        if (event.getAction() == MotionEvent.ACTION_DOWN) {

            mCanvas.drawColor(0xFFAAAAAA);
            mTouch.x = event.getX();
            mTouch.y = event.getY();
            startX = event.getX();

            if (calcCornerXY(mTouch.x, mTouch.y) == false) return false;

            this.postInvalidate();
        }
        if (event.getAction() == MotionEvent.ACTION_UP) {

            // 判断是否翻页
            if (isRight) {
                if ((event.getX() - startX) < (Kin.mHeight / 3)) {
                    mTouch.x = -(Kin.mHeight * 2);
                    mTouch.y = mCornerY;

                    // 因为向右翻页page-- 所以需要加回来
                    Kin.curPage++;

                    isTuring = false;
                } else isTuring = true;
            } else {
                if ((startX - event.getX()) < (Kin.mHeight / 3)) {
                    mTouch.x = mCornerX;
                    mTouch.y = mCornerY;

                    isTuring = false;
                } else isTuring = true;
            }

            if (isTuring) turnPage();
            else this.postInvalidate();
        }
        return true;
    }

    /**
     * Author : hmg25 Version: 1.0 Description : 求解直线P1P2和直线P3P4的交点坐标
     */
    public PointF getCross(PointF P1, PointF P2, PointF P3, PointF P4) {
        PointF CrossP = new PointF();
        // 二元函数通式： y=ax+b
        float a1 = (P2.y - P1.y) / (P2.x - P1.x);
        float b1 = ((P1.x * P2.y) - (P2.x * P1.y)) / (P1.x - P2.x);

        float a2 = (P4.y - P3.y) / (P4.x - P3.x);
        float b2 = ((P3.x * P4.y) - (P4.x * P3.y)) / (P3.x - P4.x);
        CrossP.x = (b2 - b1) / (a1 - a2);
        CrossP.y = a1 * CrossP.x + b1;
        return CrossP;
    }

    private void calcPoints() {
        mMiddleX = (mTouch.x + mCornerX) / 2;
        mMiddleY = (mTouch.y + mCornerY) / 2;
        mBezierControl1.x = mMiddleX - (mCornerY - mMiddleY) * (mCornerY - mMiddleY) / (mCornerX - mMiddleX);
        mBezierControl1.y = mCornerY;
        mBezierControl2.x = mCornerX;
        mBezierControl2.y = mMiddleY - (mCornerX - mMiddleX) * (mCornerX - mMiddleX) / (mCornerY - mMiddleY);

        mBezierStart1.x = mBezierControl1.x - (mCornerX - mBezierControl1.x) / 2;
        mBezierStart1.y = mCornerY;

        mBezierStart2.x = mCornerX;
        mBezierStart2.y = mBezierControl2.y - (mCornerY - mBezierControl2.y) / 2;

        mTouchToCornerDis = (float) Math.hypot((mTouch.x - mCornerX), (mTouch.y - mCornerY));

        mBezierEnd1 = getCross(mTouch, mBezierControl1, mBezierStart1, mBezierStart2);
        mBezierEnd2 = getCross(mTouch, mBezierControl2, mBezierStart1, mBezierStart2);

        /*
         * mBeziervertex1.x 推导 ((mBezierStart1.x+mBezierEnd1.x)/2+mBezierControl1.x)/2 化简等价于 (mBezierStart1.x+
         * 2*mBezierControl1.x+mBezierEnd1.x) / 4
         */
        mBeziervertex1.x = (mBezierStart1.x + 2 * mBezierControl1.x + mBezierEnd1.x) / 4;
        mBeziervertex1.y = (2 * mBezierControl1.y + mBezierStart1.y + mBezierEnd1.y) / 4;
        mBeziervertex2.x = (mBezierStart2.x + 2 * mBezierControl2.x + mBezierEnd2.x) / 4;
        mBeziervertex2.y = (2 * mBezierControl2.y + mBezierStart2.y + mBezierEnd2.y) / 4;
    }

    private void drawCurrentPageArea(Canvas canvas, Bitmap bitmap, Path path) {
        mPath0.reset();
        mPath0.moveTo(mBezierStart1.x, mBezierStart1.y);
        mPath0.quadTo(mBezierControl1.x, mBezierControl1.y, mBezierEnd1.x, mBezierEnd1.y);
        mPath0.lineTo(mTouch.x, mTouch.y);
        mPath0.lineTo(mBezierEnd2.x, mBezierEnd2.y);
        mPath0.quadTo(mBezierControl2.x, mBezierControl2.y, mBezierStart2.x, mBezierStart2.y);
        mPath0.lineTo(mCornerX, mCornerY);
        mPath0.close();

        canvas.save();
        canvas.clipPath(path, Region.Op.XOR);
        canvas.drawBitmap(bitmap, 0, 0, null);
        canvas.restore();
    }

    private void drawNextPageAreaAndShadow(Canvas canvas, Bitmap bitmap) {
        mPath1.reset();
        mPath1.moveTo(mBezierStart1.x, mBezierStart1.y);
        mPath1.lineTo(mBeziervertex1.x, mBeziervertex1.y);
        mPath1.lineTo(mBeziervertex2.x, mBeziervertex2.y);
        mPath1.lineTo(mBezierStart2.x, mBezierStart2.y);
        mPath1.lineTo(mCornerX, mCornerY);
        mPath1.close();

        mDegrees = (float) Math.toDegrees(Math.atan2(mBezierControl1.x - mCornerX, mBezierControl2.y - mCornerY));
        int leftx;
        int rightx;
        GradientDrawable mBackShadowDrawable;
        if (mIsRTandLB) {
            leftx = (int) (mBezierStart1.x);
            rightx = (int) (mBezierStart1.x + mTouchToCornerDis / 4);
            mBackShadowDrawable = mBackShadowDrawableLR;
        } else {
            leftx = (int) (mBezierStart1.x - mTouchToCornerDis / 4);
            rightx = (int) mBezierStart1.x;
            mBackShadowDrawable = mBackShadowDrawableRL;
        }
        canvas.save();
        canvas.clipPath(mPath0);
        canvas.clipPath(mPath1, Region.Op.INTERSECT);
        canvas.drawBitmap(bitmap, 0, 0, null);
        canvas.rotate(mDegrees, mBezierStart1.x, mBezierStart1.y);
        mBackShadowDrawable.setBounds(leftx, (int) mBezierStart1.y, rightx, (int) (mMaxLength + mBezierStart1.y));
        mBackShadowDrawable.draw(canvas);
        canvas.restore();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawColor(0xFFAAAAAA);
        calcPoints();
        drawCurrentPageArea(mCanvas, mCurPageBitmap, mPath0);
        drawNextPageAreaAndShadow(mCanvas, mNextPageBitmap);
        drawCurrentPageShadow(mCanvas);
        drawCurrentBackArea(mCanvas, mCurPageBitmap);
        canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
    }

    /**
     * Author : hmg25 Version: 1.0 Description : 创建阴影的GradientDrawable
     */
    private void createDrawable() {
        int[] color = { 0x333333, 0xb0333333 };
        mFolderShadowDrawableRL = new GradientDrawable(GradientDrawable.Orientation.RIGHT_LEFT, color);
        mFolderShadowDrawableRL.setGradientType(GradientDrawable.LINEAR_GRADIENT);

        mFolderShadowDrawableLR = new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT, color);
        mFolderShadowDrawableLR.setGradientType(GradientDrawable.LINEAR_GRADIENT);

        mBackShadowColors = new int[] { 0xff111111, 0x111111 };
        mBackShadowDrawableRL = new GradientDrawable(GradientDrawable.Orientation.RIGHT_LEFT, mBackShadowColors);
        mBackShadowDrawableRL.setGradientType(GradientDrawable.LINEAR_GRADIENT);

        mBackShadowDrawableLR = new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT, mBackShadowColors);
        mBackShadowDrawableLR.setGradientType(GradientDrawable.LINEAR_GRADIENT);

        mFrontShadowColors = new int[] { 0x80111111, 0x111111 };
        mFrontShadowDrawableVLR = new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT, mFrontShadowColors);
        mFrontShadowDrawableVLR.setGradientType(GradientDrawable.LINEAR_GRADIENT);
        mFrontShadowDrawableVRL = new GradientDrawable(GradientDrawable.Orientation.RIGHT_LEFT, mFrontShadowColors);
        mFrontShadowDrawableVRL.setGradientType(GradientDrawable.LINEAR_GRADIENT);

        mFrontShadowDrawableHTB = new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM, mFrontShadowColors);
        mFrontShadowDrawableHTB.setGradientType(GradientDrawable.LINEAR_GRADIENT);

        mFrontShadowDrawableHBT = new GradientDrawable(GradientDrawable.Orientation.BOTTOM_TOP, mFrontShadowColors);
        mFrontShadowDrawableHBT.setGradientType(GradientDrawable.LINEAR_GRADIENT);
    }

    /**
     * Author : hmg25 Version: 1.0 Description : 绘制翻起页的阴影
     */
    public void drawCurrentPageShadow(Canvas canvas) {
        double degree;
        if (mIsRTandLB) {
            degree = Math.PI / 4 - Math.atan2(mBezierControl1.y - mTouch.y, mTouch.x - mBezierControl1.x);
        } else {
            degree = Math.PI / 4 - Math.atan2(mTouch.y - mBezierControl1.y, mTouch.x - mBezierControl1.x);
        }
        // 翻起页阴影顶点与touch点的距离
        double d1 = (float) 25 * 1.414 * Math.cos(degree);
        double d2 = (float) 25 * 1.414 * Math.sin(degree);
        float x = (float) (mTouch.x + d1);
        float y;
        if (mIsRTandLB) {
            y = (float) (mTouch.y + d2);
        } else {
            y = (float) (mTouch.y - d2);
        }
        mPath1.reset();
        mPath1.moveTo(x, y);
        mPath1.lineTo(mTouch.x, mTouch.y);
        mPath1.lineTo(mBezierControl1.x, mBezierControl1.y);
        mPath1.lineTo(mBezierStart1.x, mBezierStart1.y);
        mPath1.close();
        float rotateDegrees;
        canvas.save();

        canvas.clipPath(mPath0, Region.Op.XOR);
        canvas.clipPath(mPath1, Region.Op.INTERSECT);
        int leftx;
        int rightx;
        GradientDrawable mCurrentPageShadow;
        if (mIsRTandLB) {
            leftx = (int) (mBezierControl1.x);
            rightx = (int) mBezierControl1.x + 25;
            mCurrentPageShadow = mFrontShadowDrawableVLR;
        } else {
            leftx = (int) (mBezierControl1.x - 25);
            rightx = (int) mBezierControl1.x + 1;
            mCurrentPageShadow = mFrontShadowDrawableVRL;
        }

        rotateDegrees = (float) Math.toDegrees(Math.atan2(mTouch.x - mBezierControl1.x, mBezierControl1.y - mTouch.y));
        canvas.rotate(rotateDegrees, mBezierControl1.x, mBezierControl1.y);
        mCurrentPageShadow.setBounds(leftx, (int) (mBezierControl1.y - mMaxLength), rightx, (int) (mBezierControl1.y));
        mCurrentPageShadow.draw(canvas);
        canvas.restore();

        mPath1.reset();
        mPath1.moveTo(x, y);
        mPath1.lineTo(mTouch.x, mTouch.y);
        mPath1.lineTo(mBezierControl2.x, mBezierControl2.y);
        mPath1.lineTo(mBezierStart2.x, mBezierStart2.y);
        mPath1.close();
        canvas.save();
        canvas.clipPath(mPath0, Region.Op.XOR);
        canvas.clipPath(mPath1, Region.Op.INTERSECT);
        if (mIsRTandLB) {
            leftx = (int) (mBezierControl2.y);
            rightx = (int) (mBezierControl2.y + 25);
            mCurrentPageShadow = mFrontShadowDrawableHTB;
        } else {
            leftx = (int) (mBezierControl2.y - 25);
            rightx = (int) (mBezierControl2.y + 1);
            mCurrentPageShadow = mFrontShadowDrawableHBT;
        }
        rotateDegrees = (float) Math.toDegrees(Math.atan2(mBezierControl2.y - mTouch.y, mBezierControl2.x - mTouch.x));
        canvas.rotate(rotateDegrees, mBezierControl2.x, mBezierControl2.y);
        float temp;
        if (mBezierControl2.y < 0) temp = mBezierControl2.y - Kin.mHeight;
        else temp = mBezierControl2.y;

        int hmg = (int) Math.hypot(mBezierControl2.x, temp);
        if (hmg > mMaxLength) mCurrentPageShadow.setBounds((int) (mBezierControl2.x - 25) - hmg, leftx,
                                                           (int) (mBezierControl2.x + mMaxLength) - hmg, rightx);
        else mCurrentPageShadow.setBounds((int) (mBezierControl2.x - mMaxLength), leftx, (int) (mBezierControl2.x),
                                          rightx);

        // Log.i("hmg", "mBezierControl2.x   " + mBezierControl2.x + "  mBezierControl2.y  " + mBezierControl2.y);
        mCurrentPageShadow.draw(canvas);
        canvas.restore();
    }

    /**
     * Author : hmg25 Version: 1.0 Description : 绘制翻起页背面
     */
    private void drawCurrentBackArea(Canvas canvas, Bitmap bitmap) {
        int i = (int) (mBezierStart1.x + mBezierControl1.x) / 2;
        float f1 = Math.abs(i - mBezierControl1.x);
        int i1 = (int) (mBezierStart2.y + mBezierControl2.y) / 2;
        float f2 = Math.abs(i1 - mBezierControl2.y);
        float f3 = Math.min(f1, f2);
        mPath1.reset();
        mPath1.moveTo(mBeziervertex2.x, mBeziervertex2.y);
        mPath1.lineTo(mBeziervertex1.x, mBeziervertex1.y);
        mPath1.lineTo(mBezierEnd1.x, mBezierEnd1.y);
        mPath1.lineTo(mTouch.x, mTouch.y);
        mPath1.lineTo(mBezierEnd2.x, mBezierEnd2.y);
        mPath1.close();
        GradientDrawable mFolderShadowDrawable;
        int left;
        int right;
        if (mIsRTandLB) {
            left = (int) (mBezierStart1.x - 1);
            right = (int) (mBezierStart1.x + f3 + 1);
            mFolderShadowDrawable = mFolderShadowDrawableLR;
        } else {
            left = (int) (mBezierStart1.x - f3 - 1);
            right = (int) (mBezierStart1.x + 1);
            mFolderShadowDrawable = mFolderShadowDrawableRL;
        }
        canvas.save();
        canvas.clipPath(mPath0);
        canvas.clipPath(mPath1, Region.Op.INTERSECT);

        mPaint.setColorFilter(mColorMatrixFilter);

        float dis = (float) Math.hypot(mCornerX - mBezierControl1.x, mBezierControl2.y - mCornerY);
        float f8 = (mCornerX - mBezierControl1.x) / dis;
        float f9 = (mBezierControl2.y - mCornerY) / dis;
        mMatrixArray[0] = 1 - 2 * f9 * f9;
        mMatrixArray[1] = 2 * f8 * f9;
        mMatrixArray[3] = mMatrixArray[1];
        mMatrixArray[4] = 1 - 2 * f8 * f8;
        mMatrix.reset();
        mMatrix.setValues(mMatrixArray);
        mMatrix.preTranslate(-mBezierControl1.x, -mBezierControl1.y);
        mMatrix.postTranslate(mBezierControl1.x, mBezierControl1.y);
        canvas.drawBitmap(bitmap, mMatrix, mPaint);
        // canvas.drawBitmap(bitmap, mMatrix, null);
        mPaint.setColorFilter(null);
        canvas.rotate(mDegrees, mBezierStart1.x, mBezierStart1.y);
        mFolderShadowDrawable.setBounds(left, (int) mBezierStart1.y, right, (int) (mBezierStart1.y + mMaxLength));
        mFolderShadowDrawable.draw(canvas);
        canvas.restore();
    }

}
